/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package verificador.proposiciones;

/**
 *
 * @author 1
 */
import java.util.regex.*;
import java.util.*;

public class PrepositionManager {
    public static final int OPERATOR = 1;
    public static final int OPERAND = 2;
    public static final int RIGHT_PARENTHESIS = 3;
    public static final int LEFT_PARENTHESIS = 4;
    public static final char NEGATION = '-';
    public static final char CONJUNCTION = '&';
    public static final char DISJUNCTION = '|';
    public static final char EQUIVALENCE = '=';
    public static final char IMPLICATION = '>';
    public static final int EMPTY_STACK = '!';
    public static final int TAUTOLOGY = 5;
    public static final int CONTRADICTION = 6;
    public static final int CONTINGENCY = 7;
    private HashMap<Character,int[]> operands = new HashMap<>();
    
    public String infixToPostfix(String infix) throws Exception {
        StringBuilder sb = new StringBuilder();
        Stack<Character> stack = new Stack<>();
        checking:
        for(int i = 0;i < infix.length();i++) {
            char currentChar = infix.charAt(i);
            //dependiendo del tipo de caracter es la operación
            switch(charIdentifier(currentChar)) {
                case OPERAND: sb.append(currentChar);
                    break;
                case LEFT_PARENTHESIS:stack.push(currentChar);
                    break;
                case RIGHT_PARENTHESIS: 
                    char top = stack.empty() ? EMPTY_STACK :stack.pop();
                    while(top != '(') {
                        if( top == EMPTY_STACK) {
                            break checking;
                        } else {
                            sb.append(top);
                        }
                        top = stack.empty() ? EMPTY_STACK :stack.pop();
                    }
                    break;
                case OPERATOR:
                    char peek = stack.empty() ? EMPTY_STACK :stack.peek();
                    if( peek == '(' || stack.empty() ) {
                        stack.push( currentChar );
                        continue;
                    }
                    if( getPrecedence(currentChar) == getPrecedence(peek) ) {
                        sb.append(stack.pop());
                        stack.push(currentChar);
                        continue;                         
                    }
                    if( getPrecedence(currentChar) > getPrecedence(peek) ) {
                        stack.push(currentChar);
                        continue;                         
                    }
                    if( getPrecedence(currentChar) < getPrecedence(peek) ) {
                        while( getPrecedence(currentChar) < getPrecedence(peek) ) {
                            sb.append(stack.pop());
                            peek = stack.empty() ? EMPTY_STACK :stack.peek();
                        }
                        stack.push(currentChar);
                        continue;                         
                    }
                    break;
            }
        }
        while( !stack.empty() ){
            sb.append(stack.pop());
        }
        return  sb.toString();
    }
    
    public static int charIdentifier(char c){
        int identifier = -1;
        if(Character.isLetter(c) ){
            identifier = OPERAND;
        }
        if( String.valueOf(c).matches("-|&|=|>|\\|")){
            identifier = OPERATOR;
        }
        if( c == '('){
            identifier = LEFT_PARENTHESIS;
        }
        if( c == ')'){
            identifier = RIGHT_PARENTHESIS;
        }
        return identifier;
    }
    
    public static int getPrecedence(char operator) {
        int precedence = -1;
        switch( operator ) {
            case NEGATION: 
                precedence = 100;
                break;
            case CONJUNCTION: 
                precedence = 99;
                break;
            case DISJUNCTION: 
                precedence = 98;
                break;
            case EQUIVALENCE: 
                precedence = 96;
                break;
            case IMPLICATION: 
                precedence = 97;
                break;
        }
        return precedence;
    }
    
    public void initializeOperands(String expresion){
        ArrayList<Character> operandList = new ArrayList<>();
        for( char c : expresion.toCharArray() ){
            if( charIdentifier(c) == OPERAND ) {
                if(!operandList.contains(c))
                    operandList.add(c);
            }
        }
        int nRepeticiones = (int) Math.pow(2, operandList.size()) / 2;
        int arraySize = (int) Math.pow(2, operandList.size());
        for( int i =0; i < operandList.size(); i++ ) {
            int recorrido = 0;
            int [] operandValues = new int[arraySize];
            while(recorrido < arraySize){
                for(int j =0; j < nRepeticiones;j++ ){
                    operandValues[recorrido] = 0;
                    recorrido++;
                }
                for(int j =0; j < nRepeticiones;j++ ){
                    operandValues[recorrido] = 1;
                    recorrido++;
                }
            }
            this.operands.put(operandList.get(i), operandValues); 
            nRepeticiones /= 2;
        }
    }
    public int [] getOperandValues(Character c){
        return this.operands.get(c);
    }
    
    public int[] negation( int[] operandValues){
        int[] result = new int[operandValues.length];
        int i = 0;
        for(int value : operandValues) {
            if( value == 1) {
                result[i] = 0;
            } else {
                result[i] = 1;
            }
            i++;
        }
        return result;
    }
    public int[] conjunction( int[] operandValues1,int[] operandValues2){
        int[] result = new int[operandValues1.length];
        int i = 0;
        for(int value : operandValues1) {
            if( value == operandValues2[i] && value == 1) {
                result[i] = 1;
            } else {
                result[i] = 0;
            }
            i++;
        }
        return result;
    }
    public int[] disjunction( int[] operandValues1,int[] operandValues2){
        int[] result = new int[operandValues1.length];
        int i = 0;
        for(int value : operandValues1) {
            if( operandValues2[i]  == 1 || value == 1) {
                result[i] = 1;
            } else {
                result[i] = 0;
            }
            i++;
        }
        return result;
    }
    public int[] implication( int[] operandValues1,int[] operandValues2){
        int[] result = new int[operandValues1.length];
        int i = 0;
        for(int value : operandValues1) {
            if( value == 1 && operandValues2[i] == 0 ) {
                result[i] = 0;
            } else {
                result[i] = 1;
            }
            i++;
        }
        return result;
    }
    public int[] equivalence( int[] operandValues1,int[] operandValues2){
        int[] result = new int[operandValues1.length];
        int i = 0;
        for(int value : operandValues1) {
            if( value == operandValues2[i] ) {
                result[i] = 1;
            } else {
                result[i] = 0;
            }
            i++;
        }
        return result;
    }
    
    public int[] postfixEvaluation(String postfix) {
        Stack<int[]> stack = new Stack<>();
        this.initializeOperands(postfix);
        int[] operand1, operand2, result;
        boolean expresionMalformada = false;
        checking:
        for (int i = 0; i < postfix.length(); i++) {
            char currentChar = postfix.charAt(i);
            //dependiendo del tipo de caracter es la operación
            switch (charIdentifier(currentChar)) {
                case OPERAND:
                    operand1 = this.getOperandValues(currentChar);
                    stack.push(operand1);
                    continue;
                case OPERATOR:
                    operand1 = stack.empty() ? null : stack.pop();
                    //malformado
                    if (operand1 == null) {
                        expresionMalformada = true;
                        break checking;
                    }
                    //dependiendo del tipo de operador se ejecuta la operación adecuada
                    switch (currentChar) {
                        case CONJUNCTION:
                            operand2 = stack.empty() ? null : stack.pop();
                            if (operand2 == null) {
                                expresionMalformada = true;
                                break checking;
                            }
                            result = this.conjunction(operand1, operand2);
                            stack.push(result);
                            break;
                        case DISJUNCTION:
                            operand2 = stack.empty() ? null : stack.pop();
                            if (operand2 == null) {
                                expresionMalformada = true;
                                break checking;
                            }
                            result = this.disjunction(operand1, operand2);
                            stack.push(result);
                            break;
                        case IMPLICATION:
                            operand2 = stack.empty() ? null : stack.pop();
                            if (operand2 == null) {
                                expresionMalformada = true;
                                break checking;
                            }
                            result = this.implication(operand2, operand1);
                            stack.push(result);
                            break;
                        case EQUIVALENCE:
                            operand2 = stack.empty() ? null : stack.pop();
                            if (operand2 == null) {
                                expresionMalformada = true;
                                break checking;
                            }
                            result = this.equivalence(operand1, operand2);
                            stack.push(result);
                            break;
                        case NEGATION:
                            result = this.negation(operand1);
                            stack.push(result);
                            break;
                    }
            }
        }
        result = stack.empty() ? null : stack.pop();
        if( !stack.empty() || expresionMalformada ) {
            result = null;
        }
        return result;
    }
    public int logicalEquivalence( int [] values){
        int first = values[0];
        for( int value : values ) {
            if( first != value) {
                return CONTINGENCY;
            }
        }
        if( first == 1) {
            return TAUTOLOGY;
        }
        return CONTRADICTION;
    }
}
